#ifndef __PARTICLEEMITTER_H__
#define __PARTICLEEMITTER_H__

#include "myFramework.h"
#include "Graphics/Renderable.h"
#include "Graphics/Texture.h"
#include "Utils/ObjectPool.h"
#include "Utils/Curve.h"

#if _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4251)
#endif

namespace RayFrame
{
  struct DLL_DECLARE ParticleParameter
  {
    ParticleParameter()
    {
      memset(this, 0, sizeof(ParticleParameter));
    }

    //Emitter:
    int Count;
    float ParticleLifeTime, ParticleLifeTimeVar;
    glm::vec3 EmitterPos;

    //Spawning:
    glm::vec3 PosOffset, RandomOffset;

    //Angles:
    float EmitAngle, EmitAngleVar;
    Curve EmitAngleCurv;

    //Appearance:
    typedef enum _FACING
    {
      Camera,
      Free,
      Velocity,
      Horizontal,
      Water,
      Terrain,
      Decal,
    } FACING;
    FACING Facing;

    typedef enum _BLENDTYPE
    {
      AlphaBlend,
      ColorBlend,
      Addictive,
      None,
    } BLENDTYPE;
    BLENDTYPE BlendType;

    //Texture
    Texture *TextureRef, *TextureRef2;
    int TilesX, TilesY;
    int VariableCount;
    int AnimFramesCount;

    // Appearance
    float Alpha, AlphaVar;
    Curve AlphaCurv;
    glm::vec4 Color, ColorVar;
    Curve ColorCurv;

    //Size:
    float Size, SizeRandom, SizeCurv;
    float Stretch, StretchRandom, StretchCurv;

    //Movement:
    glm::vec3 Speed, SpeedCur;
    float SpeedRandom;
    float AirResistance, AirResistanceRandom, AirResistanceCurv;
    float Gravity, GravityRandom, GravityCurv;

    glm::vec3 Acceleration;
    float TurbulenceSize, TurbulenceSpeed;

    //Rotation:
    glm::vec3 InitAngles, RandomAngles, RotationRate;
    float RandomRotationRate;
  };

  class DLL_DECLARE Particle
  {
  public:
    Particle(){};
    ~Particle(){};

    virtual void Create()
    {
    };

    virtual void Destroy()
    {
    };

    virtual void internalDestroy(){};
    virtual void internalCreate(){};

    void Reset(ParticleParameter* p);
    void Update(float timeElapsed);

    ParticleParameter* Param;

    glm::vec3 Poistion;
    glm::vec3 Direction;
    float     Alpha;
    glm::vec4 Color;

    float Size;
    glm::vec3 Rotation, RotationSpeed;

    float LifeUsed, Life;

    bool operator == (const Particle& rhs)
    {
      // TODO: more robust way to compare?
      return Poistion == rhs.Poistion && Direction == rhs.Direction &&
        Alpha == rhs.Alpha && Color == rhs.Color;
    }
  };

  class DLL_DECLARE compareParticle2
  {
  public:
    bool operator ()( Particle*& a1,  Particle*& a2) const
    {
      return a1->LifeUsed > a2->LifeUsed;
    }
  };

  class Effect;
  class RenderTarget;

  class DLL_DECLARE ParticleEmitter: public Entity
  {
  protected:
    Pool<Particle>        m_particles;
    ParticleParameter     m_parameter;

  public:
    ParticleEmitter(int particleNumber = 50);
    ~ParticleEmitter(void);

    ParticleParameter* GetParameterRef(){ return &m_parameter; };
    void SetParameter(ParticleParameter param){ m_parameter = param; };

    virtual void Update(double elapsedTime);
    virtual void BeforeRender();
    virtual void Render(double elapsedTime);
    virtual void Render(double elapsedTime, Effect* effect, RenderTarget* rt,
      Texture* texDepth = NULL);
    virtual void Render2D(double timeElapsed){};
    virtual void AfterRender();
  protected:
    void create(int particleNumber);

    void getBillboardMatrix(glm::mat4& mat, glm::vec3 pos);
    void updateParticle(double elapsedTime);
    void updateAppearance(double elapsedTime);
    virtual Particle* spawnParticle(double elapsedTime);
  private:
    double m_emitTimeAccumulate;
  };
}

#if _MSC_VER
#pragma warning(pop)
#endif

#endif
